package QianFeng04;

/**内部类：在一个类中定义的一个类
 * 成员内部类：直接在类中定义的类
 *
 * 方法内部类：在一个类中的方法内定义一个类
 *      1、方法内部类对象不能使用该方法中非final局部变量
 *      2、方法内部类只能在定义该内部类的方法内实例化，不可以在此方法外对其实例化。
 *
 * 静态内部类：在一个类内部定义一个静态内部类
 *      1、静态的含义是该内部类可以像其他静态成员一样，没有外部类对象时，也能够访问它。
 *      2、静态嵌套类仅能访问外部类的静态成员和方法。
 *
 * 匿名内部类：就是没有名字的内部类
 *  三种情况：
 *      1、继承式的匿名内部类
 *      2、接口式的匿名内部类
 *      3、参数式的匿名内部类
 * 匿名内部类原则：
 *      (1)不能有构造方法，只能有一个实例。
 *      (2)不能定义任何静态成员、静态方法。
 *      (3)不能是public,protected,private,static.
 *      (4)一定是在new的后面，用其隐含实现一个接口或实现一个类。
 *      (5)匿名内部类为局部的，所以局部内部类的所有限制都对其生效。
 *
 * 内部类的作用：
 * 每个内部类都育昆独立地继承自一个(接口的)实现，所以无论外部类是否已经继承了某个(接口的)实现，对于内部类都没有影响。
 * 如果没有内部类提供的可以继承多个具体的或抽象的类的能力，一些设计与编程问题就很难解决。
 * 从这个角度看，内部类使得多重继承的解决方案变得完整。
 * 接口解决了部分问题，而内部类有效地实现了“多重继承”。
 *
 *
 * 开发中如何选择内部类：
 * 成员：
 *    成员内部类
 *    静态内部类
 * 局部：
 *    方法内部类
 *    匿名内部类
 * 依赖外部类对象的，成员内部类，方法内部类，匿名内部类
 * 静态内部类不依赖外部类的对象。所以，我们在项目中优先考虑选择静态内部类（不会产生内存泄漏）
 *
 **/
public class Test13 {
    public static void main(String[] args) {
        Outer outer = new Outer();
        //在外部创建成员内部类的实例，因为成员内部类需要依赖外部类的对象（不建议这样实例化内部类）
        //Outer.Inner inner = outer.new Inner();
        //inner.print();
        //成员内部类
        outer.innerPrint();//成员内部类转换调用
        outer.show();//调用方法中内部类
        //静态内部类
        Outer.Inner3 inner3 = new Outer.Inner3();//不需要创建外部类对象
        inner3.print();
        //匿名内部类
        outer.print1();
        outer.print2();
        //参数式匿名内部类
        outer.print3(new Eat() {
            @Override
            public void eat() {
                System.out.println("参数式匿名内部类");
            }
        });
    }
}

class Outer{
    private String name = "Outer";

    /*------------------成员内部类------------------*/
    private class Inner{
        public void print(){
            System.out.println("成员内部类");
        }
    }
    //建议在外部类中定义一个方法，对外提供访问接口
    void innerPrint(){
        Inner inner = new Inner();
        inner.print();
    }

    /*------------------方法内部类------------------*/
    //show方法的局部变量或方法的参数，实际必须是常量 final
    public void show(){
        final int x = 10;//JDK1.8默认使用final，所以可以不写final
        class Inner2{
            public void print(){
                //x++; 从内部类引用的本地变量必须是最终变量或实际上的最终变量
                System.out.println("方法内部类"+ x);
            }
        }
        Inner2 inner2 = new Inner2();
        inner2.print();
    }

    /*------------------静态内部类------------------*/
    static class Inner3{
        public void print(){
            //System.out.println("静态内部类"+name); 不能引用非静态变量
            System.out.println("静态内部类");
        }
    }

    /*------------------匿名内部类------------------*/
    //1.继承式匿名内部类
    public void print1(){
        Cat cat = new Cat() {
            @Override
            public void eat() {
                System.out.println("cat:继承式匿名内部类");
            }
        };
        cat.eat();
    }
    //2.接口式匿名内部类
    public void print2() {
        Eat eat = new Eat() {
            @Override
            public void eat() {
                System.out.println("eat:接口式匿名内部类");
            }
        };
        eat.eat();
    }
    //3.参数式匿名内部类
    public void print3(Eat eat){

    }

}

abstract class Cat{
    public abstract void eat();
}

interface Eat{
    void eat();
}